package services.book;

import java.util.List;

import manager.ReservationManager;

import org.springframework.transaction.annotation.Transactional;

import repositories.ReservationRepository;
import services.GenericService;
import services.user.LoanService;
import services.user.PersonService;
import users.Person;
import books.Book;
import books.BookInfo;
import books.BookRequestsInfo;
import books.BookReservation;
import dtos.BookInfoDTO;

/**
 */
public class ReservationService extends GenericService {

    private static final long serialVersionUID = 1L;

    private ReservationRepository reservationRepository;

    private BookService bookService;

    private LoanService loanService;

    private PersonService personService;

    public PersonService getPersonService() {
        return this.personService;
    }

    public void setPersonService(final PersonService personService) {
        this.personService = personService;
    }

    private ReservationManager reservationManager;

    public BookService getBookService() {
        return this.bookService;
    }

    public LoanService getLoanService() {
        return this.loanService;
    }

    public void setLoanService(final LoanService loanService) {
        this.loanService = loanService;
    }

    public void setBookService(final BookService bookService) {
        this.bookService = bookService;
    }

    public ReservationRepository getReservationRepository() {
        return this.reservationRepository;
    }

    public void setReservationRepository(final ReservationRepository reservationRepository) {
        this.reservationRepository = reservationRepository;
    }

    public void setReservationManager(final ReservationManager reservationManager) {
        this.reservationManager = reservationManager;
    }

    public ReservationManager getReservationManager() {
        return this.reservationManager;
    }

    @Transactional
    public BookRequestsInfo getRequestsOf(final BookInfo aBookInfo) {
        return this.getReservationRepository().getRequestsOf(aBookInfo);
    }

    @Transactional
    public void addBookInfoRequests(final BookRequestsInfo requests) {
        this.getReservationRepository().save(requests);
    }

    @Transactional
    public void addToNotificationList(final Person aPerson, final BookInfo aBookInfo) {
        // por no tener dto
        Person personToSave = this.getPersonService().findById(aPerson.getId());

        this.getRequestsOf(aBookInfo).addPersonToNotificationList(personToSave);
    }

    @Transactional
    public void reserveTo(final BookReservation aReservation) {
        // validar reserva
        this.validateReservation(aReservation);

        // cosas locas por no tener DTO
        BookInfo toSave = this.getBookService().findBookByIsbn(aReservation.getBook().getIsbn());
        Person personToSave = this.getPersonService().findById(aReservation.getPerson().getId());
        aReservation.setBook(toSave);
        aReservation.setPerson(personToSave);
        if (aReservation.isSatisfied()) {
            Book copyToSave = this.bookService.getCopyById(aReservation.getReservedCopy().getId());
            copyToSave.setInfo(toSave);
            aReservation.setReservedCopy(copyToSave);
        }

        this.getRequestsOf(aReservation.getBook()).reserveTo(aReservation);
    }

    private void validateReservation(final BookReservation aReservation) {

        this.getReservationManager().validateReservationOrLoanFor(aReservation.getPerson());
    }

    @Transactional
    public void clearReservation(final BookInfo aBookInfo) {
        this.getRequestsOf(aBookInfo).clearReservation();
    }

    @Transactional(readOnly = true)
    public boolean isReserved(final BookInfo aBookInfo) {
        return this.getRequestsOf(aBookInfo).isReserved();
    }

    @Transactional(readOnly = true)
    public boolean isReservedDTO(final BookInfoDTO dto) {
        BookInfo info = this.getBookService().findById(dto.getId());
        return this.getRequestsOf(info).isReserved();
    }

    @Transactional
    public BookReservation getReservation(final BookInfo aBookInfo) {
        return this.getRequestsOf(aBookInfo).getReservedTo();
    }

    @Transactional
    public void clearNotificationList(final BookInfo aBookInfo) {
        this.getRequestsOf(aBookInfo).clearNotificationList();
    }

    @Transactional(readOnly = true)
    public List<Person> getNotificationList(final BookInfo aBookInfo) {
        return this.getRequestsOf(aBookInfo).getNotificationList();
    }

    @Transactional
    public Book getReservedBook(final BookInfo aBookInfo) {
        return this.getRequestsOf(aBookInfo).getReservedCopy();
    }

    @Transactional
    public List<BookReservation> getReservationsOf(final Person aPerson) {
        return this.getReservationRepository().getReservationsOf(aPerson);
    }

    @Transactional
    private void reserveCopy(final Book aCopy) {
        // viendo si se arregla el nonuniqueobjectexception
        Book copyToReserve = this.getBookService().borrowCopy(aCopy.getInfo());
        BookInfo toSave = this.bookService.findById(aCopy.getInfo().getId());
        copyToReserve.setInfo(toSave);
        this.getRequestsOf(toSave).reserveCopy(copyToReserve);
    }

    @Transactional
    public void newCopyAvailable(final Book aCopy) {
        if (this.isReserved(aCopy.getInfo())) {
            this.reserveCopy(aCopy);
        } else {
            this.getReservationManager().newCopyAvailable(aCopy);
        }
    }

    /**
     * If a copy was reserved, its returned. Then clear the reservation.
     */
    @Transactional
    public void cancelReservation(final BookReservation aReservation) {
        // viendo si arregla el nonuniqueobjectexception
        if (aReservation.isSatisfied()) {
            Book copyToSave = this.bookService.getCopyById(aReservation.getReservedCopy().getId());
            aReservation.setReservedCopy(copyToSave);
        }

        this.getReservationManager().cancelReservation(aReservation);
    }

    @Transactional
    public void cancelExpiredReservations() {
        this.getReservationManager().cancelExpiredReservations();
    }

    @Transactional
    public List<BookReservation> getSatisfiedReservations() {
        return this.getReservationRepository().findSatisfiedReservations();
    }

    @Transactional
    public void deleteBook(final BookInfo aBookInfo) {
        this.getReservationRepository().delete(this.getRequestsOf(aBookInfo));
    }

    @Transactional(readOnly = true)
    public boolean personHasReserved(final Person aPerson, final BookInfoDTO bookDto) {
        Person person = this.getPersonService().findById(aPerson.getId());
        BookInfo info = this.getBookService().findById(bookDto.getId());
        List<BookReservation> reservations = this.getReservationsOf(person);

        boolean has = false;
        for (BookReservation reservation : reservations) {
            if (reservation.getBook().equals(info)) {
                has = true;
            }
        }
        return has;
    }

    @Transactional
    public void cancelReservationBookDTO(final BookInfoDTO dto) {
        BookInfo info = this.getBookService().findById(dto.getId());
        this.cancelReservation(this.getReservation(info));
    }

}
